﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;

namespace API
{

    public class VersionControllerSelector : DefaultHttpControllerSelector
    {
        private readonly HttpConfiguration _configuration;

        private Dictionary<string, Type> _apiControllerTypes;

        private Dictionary<string, Type> ApiControllerTypes
        {
            get { return _apiControllerTypes ?? (_apiControllerTypes = GetControllerTypes()); }
        }

        public VersionControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            _configuration = configuration;
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {            
            return GetVersionedController(request) ?? base.SelectController(request);
        }

        private HttpControllerDescriptor GetVersionedController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);

            object version;
            request.GetRouteData().Values.TryGetValue("version", out version);
                       
            var type = GetControllerTypeByVersion(version, controllerName);

            if (type == null)
            {
                return null;
            }

            return new HttpControllerDescriptor(_configuration, controllerName, type);
        }

        private Type GetControllerTypeByVersion(object version, string controllerName)
        {          
            var versionToFind = string.Format("V{0}", version.ToString().ToLower());
            var controllerNameToFind = string.Format("{0}.{1}{2}", versionToFind, controllerName, ControllerSuffix);
            Type result = ApiControllerTypes.Where(t => t.Key.ToLower().Contains(versionToFind.ToLower()) && t.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase)).Select(t => t.Value).FirstOrDefault();

            return result;
        }

        private static Dictionary<string, Type> GetControllerTypes()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            var types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix) && typeof(IHttpController).IsAssignableFrom(t)))
                .ToDictionary(t => t.FullName, t => t);

            return types;
        }
    }
}